<?xml version="1.0"?>

<xsl:stylesheet
	version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:exsl="http://exslt.org/common"
	xmlns:math="http://exslt.org/math"
	extension-element-prefixes="math">

	<!-- ***************************************** Imports ***************************************** -->

	<xsl:import href="math.max.function.xsl" />
	<xsl:import href="Variables.xslt"/>

	<!-- ***************************************** Output ***************************************** -->

	<xsl:output method ="text" indent="yes"/>
	
	<!-- ***************************************** Main ***************************************** -->
	
	<xsl:template match="/">
		<xsl:call-template name="package" />
		<xsl:call-template name="imports" />
public class <xsl:value-of select="Database/@databaseHelperName" /> extends SQLiteOpenHelper {
	<xsl:call-template name="constructor" />
	<xsl:call-template name="Methods" />
	<xsl:call-template name="onCreate" />
	<xsl:call-template name="onUpgrade" />
	<xsl:call-template name="CreateHelpers" />
}
	</xsl:template>

	<xsl:template name="package">
package <xsl:value-of select="Database/@package" />;
	</xsl:template>
	
	<xsl:template name="imports">
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
	</xsl:template>
	
	<xsl:template name="constructor">
	public <xsl:value-of select="Database/@databaseHelperName" />(Context context) {
		super(context, "<xsl:value-of select="Database/@dbFilename" />", null, <xsl:value-of select="Database/@currentVersion" />);
	}
	</xsl:template>
	
	<xsl:template name="onCreate">
		<xsl:variable name ="maxiumumVersion" select="math:max(Database/Class/Build/@version)" />
	@Override
	public void onCreate(SQLiteDatabase db) {
		onUpgrade(db, 0, <xsl:value-of select="$maxiumumVersion"/>);
	}
	</xsl:template>
	
	<xsl:template name="onUpgrade">
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		<xsl:for-each select="Database/Class">
			<xsl:call-template name="OnUpdateClassSQLGenerator">
				<xsl:with-param name="Class" select="." />
			</xsl:call-template>
		</xsl:for-each>
	}
	</xsl:template>

	<!-- ***************************************** Update SQL Generator ***************************************** -->

	<xsl:template name="OnUpdateClassSQLGenerator">
		<xsl:param name="Class" />

		<xsl:variable name="Columns">
			<xsl:call-template name="UpToDateColumns">
				<xsl:with-param name="Class" select="$Class" />
			</xsl:call-template>
		</xsl:variable>
		
		<xsl:for-each select="exsl:node-set($Columns)/*">
			<xsl:sort select="@version" order="ascending"/>
			
		if ((oldVersion &lt; <xsl:value-of select="@version"/>) &amp;&amp; (<xsl:value-of select="@version"/> &lt;= newVersion)) {
			createTable(db, "<xsl:value-of select="$Class/@name"/>");

			<xsl:for-each select="Add | Rename | Drop">
				<xsl:choose>

					<xsl:when test="name()='Add'">
						<xsl:for-each select="./*">
							<xsl:call-template name="AddColumn">
								<xsl:with-param name="Class" select="$Class" />
								<xsl:with-param name="Column" select="." />
							</xsl:call-template>
						</xsl:for-each>
					</xsl:when>

					<xsl:when test="name()='Rename'">
						<xsl:for-each select="./*">
							<xsl:call-template name="RenameColumn">
								<xsl:with-param name="Class" select="$Class" />
								<xsl:with-param name="Column" select="." />
							</xsl:call-template>
						</xsl:for-each>
					</xsl:when>

					<xsl:when test="name()='Drop'">
						<xsl:for-each select="./*">
							<xsl:call-template name="DropColumn">
								<xsl:with-param name="Class" select="$Class" />
								<xsl:with-param name="Column" select="." />
							</xsl:call-template>
						</xsl:for-each>
					</xsl:when>

				</xsl:choose>
			</xsl:for-each>
		}
		</xsl:for-each>
	</xsl:template>

	<xsl:template name="AddColumn">
		<xsl:param name="Class" />
		<xsl:param name="Column" />

		<xsl:variable name="EntityType">
			<xsl:call-template name="ElementType">
				<xsl:with-param name="Element" select="$Column" />
			</xsl:call-template>
		</xsl:variable>
		
		<xsl:choose>
			
			<xsl:when test="$EntityType=$Data or $EntityType=$Object">
			createColumn(db, "<xsl:value-of select="$Class/@name" />",  "<xsl:value-of select="@variable"/>");
			</xsl:when>

			<xsl:when test="$EntityType=$Collection">
				<xsl:variable name="CollectionTableName">
					<xsl:call-template name="CollectionVariableSQLTableName">
						<xsl:with-param name="Class" select="$Class" />
						<xsl:with-param name="CollectionType" select="child::*[1]" />
					</xsl:call-template>
				</xsl:variable>

			createTable(db, "<xsl:value-of select="$CollectionTableName" />");
			createColumn(db, "<xsl:value-of select="$CollectionTableName" />", "Parent");
			createColumn(db, "<xsl:value-of select="$CollectionTableName" />", "Child");
			</xsl:when>

		</xsl:choose>
	</xsl:template>

	<xsl:template name="RenameColumn">
		<xsl:param name="Class" />
		<xsl:param name="Column" />
	</xsl:template>

	<xsl:template name="DropColumn">
		<xsl:param name="Class" />
		<xsl:param name="Column" />
	</xsl:template>

	<!-- ***************************************** DeleteHelper ***************************************** -->

	<xsl:template name="DeleteHelper">
		<xsl:param name="Class" />

		public static final int Delete(SQLiteDatabase db, <xsl:value-of select="$Class/@name" /> value) {
		<xsl:call-template name="DeleteCollection">
			<xsl:with-param name="Class" select="$Class" />
		</xsl:call-template>
			return db.delete("<xsl:value-of select="@name" />", "_id=?", new String[] { value.<xsl:value-of select="./Key/MethodMap/@get" />().toString() });
		}

	</xsl:template>

	<xsl:template name="DeleteCollection">
		<xsl:param name="Class" />

		<xsl:variable name="Collections">
			<xsl:call-template name="SelectAllCollectionElements">
				<xsl:with-param name="Class" select="$Class" />
			</xsl:call-template>
		</xsl:variable>

		<xsl:for-each select="exsl:node-set($Collections)/*">
			<xsl:variable name="CollectionTableName">
				<xsl:call-template name="CollectionVariableSQLTableName">
					<xsl:with-param name="Class" select="$Class" />
					<xsl:with-param name="CollectionType" select="." />
				</xsl:call-template>
			</xsl:variable>
			db.delete("<xsl:value-of select="$CollectionTableName" />", "Parent=?", new String[] { value.<xsl:value-of select="$Class/Key/MethodMap/@get" />().toString() });
		</xsl:for-each>
	</xsl:template>

	<!-- ***************************************** SaveHelper ***************************************** -->

	<xsl:template name="SaveHelper">
		<xsl:param name="Class" />
		public static final long Save(SQLiteDatabase db, <xsl:value-of select="$Class/@name" /> value) {
			ContentValues values = new ContentValues();

		<xsl:call-template name="SaveData">
			<xsl:with-param name="Class" select="$Class" />
		</xsl:call-template>

			if (value.<xsl:value-of select="$Class/Key/MethodMap/@get" />() != null) {
				values.put("_id", value.<xsl:value-of select="$Class/Key/MethodMap/@get" />());
			}
			long returnValue = db.replace("<xsl:value-of select="$Class/@name" />", "_nullColumnHack", values);
			value.<xsl:value-of select="$Class/Key/MethodMap/@set" />(returnValue);

		<xsl:call-template name="SaveCollections">
			<xsl:with-param name="Class" select="$Class" />
		</xsl:call-template>

			return returnValue;
		}
	</xsl:template>

	<xsl:template name="SaveData">
		<xsl:param name="Class" />

		<xsl:variable name="Columns">
			<xsl:call-template name="SelectAllDataAndObjects">
				<xsl:with-param name="Class" select="$Class" />
			</xsl:call-template>
		</xsl:variable>
		
		<xsl:for-each select="exsl:node-set($Columns)/*">
			<xsl:variable name="ElementType">
				<xsl:call-template name ="ElementType">
					<xsl:with-param name="Element" select="."/>
				</xsl:call-template>
			</xsl:variable>
			
			<xsl:variable name="GetValue">
				<xsl:choose>
					<xsl:when test="$ElementType=$Data">
						<xsl:text>value.</xsl:text>
						<xsl:value-of select="MethodMap/@get" />
						<xsl:text>()</xsl:text>
					</xsl:when>
					<xsl:when test="$ElementType=$Object">
						<xsl:text>DatabaseHelper.</xsl:text>
						<xsl:value-of select="@class" />
						<xsl:text>Helper.Save(db, value.</xsl:text>
						<xsl:value-of select="MethodMap/@get" />
						<xsl:text>())</xsl:text>
					</xsl:when>
				</xsl:choose>
			</xsl:variable>

			values.put("<xsl:value-of select="@variable" />", <xsl:value-of select="$GetValue" />);
		</xsl:for-each>
	</xsl:template>

	<xsl:template name="SaveCollections">
		<xsl:param name="Class" />

		<xsl:variable name="Collections">
			<xsl:call-template name="SelectAllCollectionElements">
				<xsl:with-param name="Class" select="$Class" />
			</xsl:call-template>
		</xsl:variable>

		<xsl:if test="count(exsl:node-set($Collections)/*) > 0">
			ContentValues values2 = new ContentValues();
			values2.put("Parent", returnValue);
			<xsl:for-each select="exsl:node-set($Collections)/*" >
				<xsl:variable name="CollectionTableName">
					<xsl:call-template name="CollectionVariableSQLTableName">
						<xsl:with-param name="Class" select="$Class" />
						<xsl:with-param name="CollectionType" select="." />
					</xsl:call-template>
				</xsl:variable>

			db.execSQL("DELETE FROM <xsl:value-of select="$CollectionTableName"/> WHERE Parent=" + returnValue);
			if (value.<xsl:value-of select="MethodMap/@get" />() != null) {
				<xsl:variable name="ObjectName">
					<xsl:call-template name="JavaTypeFromElement">
						<xsl:with-param name="Element" select="." />
					</xsl:call-template>
				</xsl:variable>

				<xsl:variable name="ElementType">
					<xsl:call-template name="ElementType">
						<xsl:with-param name="Element" select="." />
					</xsl:call-template>
				</xsl:variable>

				for (<xsl:value-of select="$ObjectName" /> v : value.<xsl:value-of select="MethodMap/@get" />()) {
					<xsl:choose>
						<xsl:when test="$ElementType=$Data">
					values2.put("Child", v);
						</xsl:when>
						<xsl:when test="$ElementType=$Object">
					values2.put("Child", DatabaseHelper.<xsl:value-of select="@class" />Helper.Save(db, v));
						</xsl:when>
					</xsl:choose>
					db.insert("<xsl:value-of select="$CollectionTableName"/>", "_nullColumnHack", values2);
				}
			}
			</xsl:for-each>
		</xsl:if>

	</xsl:template>

	<!-- ***************************************** QueryHelper ***************************************** -->

	<xsl:template name="CreateHelpers">
		<xsl:for-each select="Database/Class">
	public final static class <xsl:value-of select="@name" />Helper  {
		public static HashMap&lt;Long, <xsl:value-of select="./@name"/>&gt; Cache = new HashMap&lt;Long,<xsl:value-of select="./@name"/>&gt;();
			<xsl:call-template name="DeleteHelper">
				<xsl:with-param name="Class" select="." />
			</xsl:call-template>
			<xsl:call-template name="SaveHelper">
				<xsl:with-param name="Class" select="." />
			</xsl:call-template>
			<xsl:call-template name="QueriesHelper">
				<xsl:with-param name="Class" select="." />
			</xsl:call-template>
	}
		</xsl:for-each>
	</xsl:template>


	<xsl:template name="QueriesHelper">
		<xsl:param name="Class" />
		public final static class Query {
		<xsl:call-template name="SelectAllQuery">
			<xsl:with-param name="Class" select="$Class" />
		</xsl:call-template>
		<xsl:call-template name="SelectByIdQuery">
			<xsl:with-param name="Class" select="$Class" />
		</xsl:call-template>
		<xsl:call-template name="GetFromCursorQuery">
			<xsl:with-param name="Class" select="$Class" />
		</xsl:call-template>
		}
	</xsl:template>

	<xsl:template name="SelectAllQuery">
		<xsl:param name="Class" />
			public static final List&lt;<xsl:value-of select="$Class/@name"/>&gt; SelectAll(SQLiteDatabase db) {
				String queryString = "SELECT * FROM <xsl:value-of select="$Class/@name"/>";
				Cursor queryCursor = db.rawQuery(queryString, null);
				return getFromCursor(db, queryCursor);
			}
	</xsl:template>

	<xsl:template name="SelectByIdQuery">
		<xsl:param name="Class" />

			public static final <xsl:value-of select="$Class/@name"/> GetById(SQLiteDatabase db, long id) {
				if (Cache.containsKey(id))
					return Cache.get(id);

				String queryString = "SELECT * FROM <xsl:value-of select="$Class/@name"/> WHERE _id = " + id;
				Cursor queryCursor = db.rawQuery(queryString, null);
				List&lt;<xsl:value-of select="$Class/@name"/>&gt; objects = getFromCursor(db, queryCursor);

				<xsl:value-of select="$Class/@name"/> returnObject;

				if (objects.size() == 0) {
					returnObject = null;
				} else {
					returnObject = objects.get(0);
				}

				Cache.put(id, returnObject);
				return returnObject;
			}
	</xsl:template>

	<xsl:template name="GetFromCursorQuery">
		<xsl:param name="Class" />
		
		<xsl:variable name="DataColumns">
			<xsl:call-template name="SelectAllDataAndObjects">
				<xsl:with-param name="Class" select="$Class" />
			</xsl:call-template>
		</xsl:variable>
		<xsl:variable name="CollectionColumns">
			<xsl:call-template name="SelectAllCollections">
				<xsl:with-param name="Class" select="$Class" />
			</xsl:call-template>
		</xsl:variable>
		
			private static List&lt;<xsl:value-of select="$Class/@name"/>&gt; getFromCursor(SQLiteDatabase db, Cursor c) {
				List&lt;<xsl:value-of select="$Class/@name"/>&gt; returnValues = new ArrayList&lt;<xsl:value-of select="$Class/@name"/>&gt;();
				if (c != null) {
					int _tableIdIndex = c.getColumnIndex("_id");

					<xsl:call-template name="GetFromCursorQuery_GetData">
						<xsl:with-param name="Class" select="$Class"/>
						<xsl:with-param name="DataColumns" select="$DataColumns" />
					</xsl:call-template>

					<xsl:call-template name="GetFromCursorQuery_GetCollections">
						<xsl:with-param name="Class" select="$Class" />
						<xsl:with-param name="CollectionColumns" select="$CollectionColumns" />
					</xsl:call-template>
				}

				return returnValues;
			}

	</xsl:template>

	<xsl:template name="GetFromCursorQuery_GetData">
		<xsl:param name="Class" />
		<xsl:param name="DataColumns" />
		
		<xsl:for-each select="exsl:node-set($DataColumns)/*">
			int <xsl:value-of select="@variable" />Index = c.getColumnIndex("<xsl:value-of select="@variable" />");
		</xsl:for-each>

		while (c.moveToNext()) {
		<xsl:value-of select="$Class/@name"/> newValue = new <xsl:value-of select="$Class/@name"/>();
		long objectId = c.getLong(_tableIdIndex);
		newValue.<xsl:value-of select="$Class/Key/MethodMap/@set" />(objectId);
		Cache.put(objectId, newValue);

		<xsl:for-each select="exsl:node-set($DataColumns)/*">

			<xsl:variable name="Element">
				<xsl:call-template name="ElementType">
					<xsl:with-param name="Element" select="."/>
				</xsl:call-template>
			</xsl:variable>

			<xsl:variable name="Nullable">
				<xsl:call-template name="IsNullable">
					<xsl:with-param name="Element" select="." />
				</xsl:call-template>
			</xsl:variable>

			<xsl:if test="$Nullable = 1">
				if (c.isNull(<xsl:value-of select="@variable" />Index))
					newValue.<xsl:value-of select="MethodMap/@set" />(null);
				else
			</xsl:if>

			<xsl:choose>
				<xsl:when test="$Element=$Data">
					<xsl:variable name="GetType">
						<xsl:call-template name="GetType">
							<xsl:with-param name="Type" select="." />
						</xsl:call-template>
					</xsl:variable>
					newValue.<xsl:value-of select="MethodMap/@set" />(c.<xsl:value-of select="$GetType" />(<xsl:value-of select="@variable" />Index));
				</xsl:when>
				<xsl:when test="$Element=$Object">
					newValue.<xsl:value-of select="MethodMap/@set" />(<xsl:value-of select="@variable" />Helper.Query.GetById(db, c.getLong(<xsl:value-of select="@variable" />Index)));
				</xsl:when>
			</xsl:choose>
		</xsl:for-each>
		returnValues.add(newValue);
		}
	</xsl:template>

	<xsl:template name="GetFromCursorQuery_GetCollections">
		<xsl:param name="CollectionColumns" />
		<xsl:param name="Class" />
		<xsl:if test="count(exsl:node-set($CollectionColumns)/*) > 0">
			String formatted;
			Cursor listC;
			for (<xsl:value-of select="$Class/@name"/> object : returnValues) {
			<xsl:for-each select="exsl:node-set($CollectionColumns)/*">
				<xsl:variable name="this" select="." />
				<xsl:for-each select="./*">
					<xsl:variable name="CollectionType" select="name(.)" />

					<xsl:variable name="CollectionTableName">
						<xsl:call-template name="CollectionVariableSQLTableName">
							<xsl:with-param name="Class" select="$Class" />
							<xsl:with-param name="CollectionType" select="." />
						</xsl:call-template>
					</xsl:variable>

					<xsl:variable name="JavaType">
						<xsl:call-template name="JavaTypeFromElement">
							<xsl:with-param name="Element" select="." />
						</xsl:call-template>
					</xsl:variable>

					formatted = "SELECT * FROM <xsl:value-of select="$CollectionTableName"/> WHERE Parent = " + object.<xsl:value-of select="$Class/Key/MethodMap/@get" />();
					listC = db.rawQuery(formatted, null);
					if (listC != null) {
					int _childIndex2 = listC.getColumnIndex("Child");

					List&lt;<xsl:value-of select="$JavaType" />&gt; gotList = new ArrayList&lt;<xsl:value-of select="$JavaType" />&gt;();
					while (listC.moveToNext()) {
					<xsl:call-template name="GotListAdd">
						<xsl:with-param name="Element" select="." />
						<xsl:with-param name="JavaType" select="$JavaType" />
					</xsl:call-template>
					}

					<xsl:call-template name="SetListAdd">
						<xsl:with-param name="CollectionType" select="$this"/>
						<xsl:with-param name="JavaType" select="$JavaType" />
					</xsl:call-template>
					}
				</xsl:for-each>
			</xsl:for-each>
			}
		</xsl:if>
	</xsl:template>
	
	<!-- ***************************************** Static Methods ***************************************** -->

	<xsl:template name="Methods">
	private boolean tableExists(SQLiteDatabase db, String tablename) {
		String sqlString = "SELECT name FROM sqlite_master WHERE type='table' AND name='" + tablename + "'";
		Cursor c = db.rawQuery(sqlString, null);

		try {
			boolean thereAreRows = (c.getCount() > 0);
			return thereAreRows;
		}
		finally {
			c.close();
			c = null;
		}
	}

	private void createTable(SQLiteDatabase db, String tableName) {
		if (tableExists(db, tableName) == false) {
			String sqlString =
				"CREATE TABLE " + tableName + " ( " +
					"_id INTEGER PRIMARY KEY, " +
					"_nullColumnHack);";
			db.execSQL(sqlString);
		}
	}

	private void createColumn(SQLiteDatabase db, String tableName, String columnName) {
		db.execSQL("ALTER TABLE " + tableName + " ADD COLUMN " + columnName);
	}
	</xsl:template>

</xsl:stylesheet>
